﻿using System;
using System.Collections;
using System.Collections.Generic;
using Glue;
using Xunit;

namespace GlueTests.MapperTests
{
    public class When_mapping_between_dictionaries
    {
        private readonly Mapping<SomePerson, SomeOtherPerson> mapping;

        public class SomePerson
        {
            public IDictionary<int,string> AnIDictionary { get; set; }
            public Dictionary<string, double> ADictionary { get; set; }
            public SortedList<int, string> ASortedList { get; set; }
            public CustomDictionary<int,string> ACustomDictionary { get; set; }
            public NotInheritingIDictionary<int,string> NotInheritingIDictionary { get; set; }
        }

        public class SomeOtherPerson
        {
            public IDictionary<int, string> AnIDictionary { get; set; }
            public Dictionary<string, double> ADictionary { get; set; }
            public SortedList<int, string> ASortedList { get; set; }
            public CustomDictionary<int, string> ACustomDictionary { get; set; }
            public NotInheritingIDictionary<int, string> NotInheritingIDictionary { get; set; }
        }

        public When_mapping_between_dictionaries()
        {
            mapping = new Mapping<SomePerson, SomeOtherPerson>();
        }

        [Fact]
        public void Should_map_between_IDicitonaries()
        {
            
            mapping.Relate(x=>x.AnIDictionary,y=>y.AnIDictionary);
            var fromPerson = new SomePerson{AnIDictionary = new Dictionary<int, string>{{2,"two"},{101,"one-o-one"}}};
            var toPerson = mapping.Map(fromPerson, new SomeOtherPerson());
            Assert.Equal(2,toPerson.AnIDictionary.Count);
            Assert.Equal("two",toPerson.AnIDictionary[2]);
            Assert.Equal("one-o-one", toPerson.AnIDictionary[101]);
        }

        [Fact]
        public void Should_map_To_SortedList()
        {
            mapping.Relate(x => x.AnIDictionary, y => y.ASortedList);
            var fromPerson = new SomePerson { AnIDictionary = new Dictionary<int, string> { { 2, "two" }, { 101, "one-o-one" } } };
            var toPerson = mapping.Map(fromPerson, new SomeOtherPerson());
            var sortedList = toPerson.ASortedList;
            Assert.Equal(2, sortedList.Count);
            Assert.Equal("two", sortedList[2]);
            Assert.Equal("one-o-one", sortedList[101]);
        }

        [Fact]
        public void Should_sort_SortedList()
        {
            mapping.Relate(x => x.AnIDictionary, y => y.ASortedList);
            var fromPerson = new SomePerson { AnIDictionary = new Dictionary<int, string> { { 2, "two" }, { 1, "one" }, { 13, "th..." } } };
            var toPerson = mapping.Map(fromPerson, new SomeOtherPerson());
            var sortedList = toPerson.ASortedList;
            //Assert.Equal(3, sortedList.Count);
            Assert.Equal(0, sortedList.IndexOfKey(1));
            Assert.Equal(1, sortedList.IndexOfKey(2));
            Assert.Equal(2, sortedList.IndexOfKey(13));
        }

        [Fact]
        public void Should_map_from_customDictionary()
        {
            mapping.Relate(x=>x.ACustomDictionary,y=>y.AnIDictionary);
            var  fromPerson = new SomePerson { ACustomDictionary = new CustomDictionary<int, string> { { 2, "two" }, { 1, "one" }, { 13, "th..." } } };
            var toPerson = mapping.Map(fromPerson, new SomeOtherPerson());
            Assert.Equal(3, toPerson.AnIDictionary.Count);
            Assert.Equal("two", toPerson.AnIDictionary[2]);
            Assert.Equal("one", toPerson.AnIDictionary[1]);
            Assert.Equal("th...", toPerson.AnIDictionary[13]);
        }

        [Fact]
        public void Should_map_to_customDictionary()
        {
            mapping.Relate(x => x.AnIDictionary, y => y.ACustomDictionary);
            var fromPerson = new SomePerson { AnIDictionary = new Dictionary<int, string> { { 2, "two" }, { 1, "one" }, { 13, "th..." } } };
            var toPerson = mapping.Map(fromPerson, new SomeOtherPerson());
            Assert.Equal(3, toPerson.ACustomDictionary.Count);
            Assert.Equal("two", toPerson.ACustomDictionary[2]);
            Assert.Equal("one", toPerson.ACustomDictionary[1]);
            Assert.Equal("th...", toPerson.ACustomDictionary[13]);
        }

        [Fact]
        public void Should_map_with_other_types_for_key_and_value()
        {
            mapping.Relate(x => x.ADictionary, y => y.ADictionary);
            var fromPerson = new SomePerson { ADictionary = new Dictionary<string, double> { { "two",2 }, { "one-o-one",101.1 } } };
            var toPerson = mapping.Map(fromPerson, new SomeOtherPerson());
            Assert.Equal(2, toPerson.ADictionary.Count);
            Assert.Equal(2, toPerson.ADictionary["two"]);
            Assert.Equal(101.1, toPerson.ADictionary["one-o-one"]);
        }

        [Fact]
        public void Should_throw_friendly_exception_when_not_inheriting_IDictionary()
        {
            Assert.Throws(typeof (GlueException), () =>
              {
                  mapping.Relate(x => x.NotInheritingIDictionary,
                                 y => y.ADictionary);
                  var fromPerson = new SomePerson {NotInheritingIDictionary = new NotInheritingIDictionary<int, string>{{2, "two"}}};
                  mapping.Map(fromPerson, new SomeOtherPerson());
              });
        }


        #region Custom Dictionaries
        public class CustomDictionary<TKey, TValue> : IDictionary<TKey, TValue>,IDictionary
        {
            private Dictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue>();

            public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
            {
                return dictionary.GetEnumerator();
            }

            public void Remove(object key)
            {
                throw new System.NotImplementedException();
            }

            object IDictionary.this[object key]
            {
                get { throw new System.NotImplementedException(); }
                set { throw new System.NotImplementedException(); }
            }

            ICollection IDictionary.Keys
            {
                get { throw new System.NotImplementedException(); }
            }

            ICollection IDictionary.Values
            {
                get { throw new System.NotImplementedException(); }
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            public void Add(KeyValuePair<TKey, TValue> item)
            {
                dictionary.Add(item.Key, item.Value);
            }

            public bool Contains(object key)
            {
                throw new System.NotImplementedException();
            }

            public void Add(object key, object value)
            {
                dictionary.Add((TKey)key,(TValue)value);
            }

            public void Clear()
            {
                dictionary.Clear();
            }

            IDictionaryEnumerator IDictionary.GetEnumerator()
            {
                throw new System.NotImplementedException();
            }

            public bool Contains(KeyValuePair<TKey, TValue> item)
            {
                return dictionary.ContainsKey(item.Key);
            }

            public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
            {
                throw new System.NotImplementedException();
            }

            public bool Remove(KeyValuePair<TKey, TValue> item)
            {
                return dictionary.Remove(item.Key);
            }

            public void CopyTo(Array array, int index)
            {
                throw new System.NotImplementedException();
            }

            public int Count
            {
                get { return dictionary.Count; }
            }

            public object SyncRoot
            {
                get { throw new System.NotImplementedException(); }
            }

            public bool IsSynchronized
            {
                get { throw new System.NotImplementedException(); }
            }

            public bool IsReadOnly
            {
                get { throw new System.NotImplementedException(); }
            }

            public bool IsFixedSize
            {
                get { throw new System.NotImplementedException(); }
            }

            public bool ContainsKey(TKey key)
            {
                return dictionary.ContainsKey(key);
            }

            public void Add(TKey key, TValue value)
            {
                dictionary.Add(key, value);
            }

            public bool Remove(TKey key)
            {
                return dictionary.Remove(key);
            }

            public bool TryGetValue(TKey key, out TValue value)
            {
                return dictionary.TryGetValue(key, out value);
            }

            public TValue this[TKey key]
            {
                get { return dictionary[key]; }
                set { dictionary[key] = value; }
            }

            public ICollection<TKey> Keys
            {
                get { return dictionary.Keys; }
            }

            public ICollection<TValue> Values
            {
                get { return dictionary.Values; }
            }
        }

        public class NotInheritingIDictionary<TKey, TValue> : IDictionary<TKey, TValue>
        {
            public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
            {
                throw new System.NotImplementedException();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            public void Add(KeyValuePair<TKey, TValue> item)
            {
                throw new System.NotImplementedException();
            }

            public void Clear()
            {
                throw new System.NotImplementedException();
            }

            public bool Contains(KeyValuePair<TKey, TValue> item)
            {
                throw new System.NotImplementedException();
            }

            public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
            {
                throw new System.NotImplementedException();
            }

            public bool Remove(KeyValuePair<TKey, TValue> item)
            {
                throw new System.NotImplementedException();
            }

            public int Count
            {
                get { throw new System.NotImplementedException(); }
            }

            public bool IsReadOnly
            {
                get { throw new System.NotImplementedException(); }
            }

            public bool ContainsKey(TKey key)
            {
                throw new System.NotImplementedException();
            }

            public void Add(TKey key, TValue value)
            {
                throw new System.NotImplementedException();
            }

            public bool Remove(TKey key)
            {
                throw new System.NotImplementedException();
            }

            public bool TryGetValue(TKey key, out TValue value)
            {
                throw new System.NotImplementedException();
            }

            public TValue this[TKey key]
            {
                get { throw new System.NotImplementedException(); }
                set { throw new System.NotImplementedException(); }
            }

            public ICollection<TKey> Keys
            {
                get { throw new System.NotImplementedException(); }
            }

            public ICollection<TValue> Values
            {
                get { throw new System.NotImplementedException(); }
            }
        }
#endregion
    }
}
